(ns server
    (:import [java.io InputStream OutputStream BufferedReader InputStreamReader PrintWriter]
			 [java.net ServerSocket InetAddress]
			 [java.util Scanner])
	(:gen-class))

(def selfIP (str (. (InetAddress/getLocalHost) getHostAddress)))
(def ss nil)
(def socket nil)
(def in nil)
(def out nil)
(def client_set #{})
(def writer_set #{})
(def reader_set #{})
(def adminIP "")
(def port nil)

(defn mapcar [f arr]
	(if (not (nil? (first arr)))
		(do
			(f (first arr))
			(mapcar f (rest arr)))))

(defn init []
	(let [inp (Scanner. System/in)]
		(print "Set Your Port: ") (.flush *out*)
		(def port (. inp nextInt))
		(def ss (ServerSocket. port))
		(print "Set Admin IP: ")  (.flush *out*)
		(loop []
			(def adminIP (str "/" (. inp nextLine)))
			(let [len (.length adminIP)]
				(if (not (and (>= len 7) (<= len 15))) ;0.0.0.0  255.255.255.255
					(recur))))
		(println (str "Set OK, Use Port " port ", Admin IP " adminIP ", ServerIP " selfIP))))

(defn send_all 
	([text addr]
		(println (str addr " : " text))
		(mapcar (fn [i] (. i println (str addr "  : " text)) (. i flush)) writer_set))
	([text]
		(println text)
		(mapcar (fn [i] (. i println text) (. i flush)) writer_set)))

(defn close_sets [name]
	(for [i name] (. i close)))

(defn sock_acc [socket out in addr]
	
	;;;unsolve: heart package which check client connect server
	
	(send_all (str addr " Upline"))
	(loop []
		  (let [line (. in readLine)]
		  	  (if (and (= (str addr) adminIP) (>= (.length line) 8) (= (subs line 0 8) "shutdown"))
	  	  	  	(do
	  	  	  		(send_all (str "Admin Close The Socket" \newline))
		  	  	  	(close_sets reader_set)
		  	  	  	(close_sets writer_set)
		  	  	  	(close_sets client_set)
		  	  	  	(System/exit 0)))
	  	  (if (or (= line "quit") (nil? line))
	  	  	  (do
	  	  	  	(send_all (str (. socket getInetAddress) " Offline"))
	  	  	  	(def client_set (disj client_set socket))
				(def writer_set (disj writer_set out))
				(def reader_set (disj reader_set in))
	  	  	  	(. out close)
			  	(. in close)
			  	(. socket close))
			  (do
				(send_all line addr)
				(recur))))))
(defn main []
	(init)
	(loop [] 
		(let [socket (. ss accept)]
			(let [writer (PrintWriter. (. socket getOutputStream) true)
				  reader (BufferedReader. (InputStreamReader. (. socket getInputStream)))
				  addr   (. socket getInetAddress)]
				(def client_set (conj client_set socket))
				(def writer_set (conj writer_set writer))
				(def reader_set (conj reader_set reader))
				(future (sock_acc socket writer reader addr))))
		(recur)))

(main)